import { Layer } from './Layer';
import * as Util from '../core/Util';
import { toLatLngBounds } from '../geo/LatLngBounds';
import { Bounds } from '../geometry/Bounds';
import * as DomUtil from '../dom/DomUtil';

/*
 * @class ImageOverlay
 * @aka L.ImageOverlay
 * @inherits Interactive layer
 *
 * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
 *
 * @example
 *
 * ```js
 * var imageUrl = 'https://maps.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
 * 	imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
 * L.imageOverlay(imageUrl, imageBounds).addTo(map);
 * ```
 */

export var ImageOverlay = Layer.extend({
  // @section
  // @aka ImageOverlay options
  options: {
    // @option opacity: Number = 1.0
    // The opacity of the image overlay.
    opacity: 1,

    // @option alt: String = ''
    // Text for the `alt` attribute of the image (useful for accessibility).
    alt: '',

    // @option interactive: Boolean = false
    // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
    interactive: false,

    // @option crossOrigin: Boolean|String = false
    // Whether the crossOrigin attribute will be added to the image.
    // If a String is provided, the image will have its crossOrigin attribute set to the String provided. This is needed if you want to access image pixel data.
    // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
    crossOrigin: false,

    // @option errorOverlayUrl: String = ''
    // URL to the overlay image to show in place of the overlay that failed to load.
    errorOverlayUrl: '',

    // @option zIndex: Number = 1
    // The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the overlay layer.
    zIndex: 1,

    // @option className: String = ''
    // A custom class name to assign to the image. Empty by default.
    className: '',
  },

  initialize: function (url, bounds, options) {
    // (String, LatLngBounds, Object)
    this._url = url;
    this._bounds = toLatLngBounds(bounds);

    Util.setOptions(this, options);
  },

  onAdd: function () {
    if (!this._image) {
      this._initImage();

      if (this.options.opacity < 1) {
        this._updateOpacity();
      }
    }

    if (this.options.interactive) {
      DomUtil.addClass(this._image, 'leaflet-interactive');
      this.addInteractiveTarget(this._image);
    }

    this.getPane().appendChild(this._image);
    this._reset();
  },

  onRemove: function () {
    DomUtil.remove(this._image);
    if (this.options.interactive) {
      this.removeInteractiveTarget(this._image);
    }
  },

  // @method setOpacity(opacity: Number): this
  // Sets the opacity of the overlay.
  setOpacity: function (opacity) {
    this.options.opacity = opacity;

    if (this._image) {
      this._updateOpacity();
    }
    return this;
  },

  setStyle: function (styleOpts) {
    if (styleOpts.opacity) {
      this.setOpacity(styleOpts.opacity);
    }
    return this;
  },

  // @method bringToFront(): this
  // Brings the layer to the top of all overlays.
  bringToFront: function () {
    if (this._map) {
      DomUtil.toFront(this._image);
    }
    return this;
  },

  // @method bringToBack(): this
  // Brings the layer to the bottom of all overlays.
  bringToBack: function () {
    if (this._map) {
      DomUtil.toBack(this._image);
    }
    return this;
  },

  // @method setUrl(url: String): this
  // Changes the URL of the image.
  setUrl: function (url) {
    this._url = url;

    if (this._image) {
      this._image.src = url;
    }
    return this;
  },

  // @method setBounds(bounds: LatLngBounds): this
  // Update the bounds that this ImageOverlay covers
  setBounds: function (bounds) {
    this._bounds = toLatLngBounds(bounds);

    if (this._map) {
      this._reset();
    }
    return this;
  },

  getEvents: function () {
    var events = {
      zoom: this._reset,
      viewreset: this._reset,
    };

    if (this._zoomAnimated) {
      events.zoomanim = this._animateZoom;
    }

    return events;
  },

  // @method setZIndex(value: Number): this
  // Changes the [zIndex](#imageoverlay-zindex) of the image overlay.
  setZIndex: function (value) {
    this.options.zIndex = value;
    this._updateZIndex();
    return this;
  },

  // @method getBounds(): LatLngBounds
  // Get the bounds that this ImageOverlay covers
  getBounds: function () {
    return this._bounds;
  },

  // @method getElement(): HTMLElement
  // Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement)
  // used by this overlay.
  getElement: function () {
    return this._image;
  },

  _initImage: function () {
    var wasElementSupplied = this._url.tagName === 'IMG';
    var img = (this._image = wasElementSupplied
      ? this._url
      : DomUtil.create('img'));

    DomUtil.addClass(img, 'leaflet-image-layer');
    if (this._zoomAnimated) {
      DomUtil.addClass(img, 'leaflet-zoom-animated');
    }
    if (this.options.className) {
      DomUtil.addClass(img, this.options.className);
    }

    img.onselectstart = Util.falseFn;
    img.onmousemove = Util.falseFn;

    // @event load: Event
    // Fired when the ImageOverlay layer has loaded its image
    img.onload = Util.bind(this.fire, this, 'load');
    img.onerror = Util.bind(this._overlayOnError, this, 'error');

    if (this.options.crossOrigin || this.options.crossOrigin === '') {
      img.crossOrigin =
        this.options.crossOrigin === true ? '' : this.options.crossOrigin;
    }

    if (this.options.zIndex) {
      this._updateZIndex();
    }

    if (wasElementSupplied) {
      this._url = img.src;
      return;
    }

    img.src = this._url;
    img.alt = this.options.alt;
  },

  _animateZoom: function (e) {
    var scale = this._map.getZoomScale(e.zoom),
      offset = this._map._latLngBoundsToNewLayerBounds(
        this._bounds,
        e.zoom,
        e.center,
      ).min;

    DomUtil.setTransform(this._image, offset, scale);
  },

  _reset: function () {
    var image = this._image,
      bounds = new Bounds(
        this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
        this._map.latLngToLayerPoint(this._bounds.getSouthEast()),
      ),
      size = bounds.getSize();

    DomUtil.setPosition(image, bounds.min);

    image.style.width = size.x + 'px';
    image.style.height = size.y + 'px';
  },

  _updateOpacity: function () {
    DomUtil.setOpacity(this._image, this.options.opacity);
  },

  _updateZIndex: function () {
    if (
      this._image &&
      this.options.zIndex !== undefined &&
      this.options.zIndex !== null
    ) {
      this._image.style.zIndex = this.options.zIndex;
    }
  },

  _overlayOnError: function () {
    // @event error: Event
    // Fired when the ImageOverlay layer fails to load its image
    this.fire('error');

    var errorUrl = this.options.errorOverlayUrl;
    if (errorUrl && this._url !== errorUrl) {
      this._url = errorUrl;
      this._image.src = errorUrl;
    }
  },

  // @method getCenter(): LatLng
  // Returns the center of the ImageOverlay.
  getCenter: function () {
    return this._bounds.getCenter();
  },
});

// @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
// Instantiates an image overlay object given the URL of the image and the
// geographical bounds it is tied to.
export var imageOverlay = function (url, bounds, options) {
  return new ImageOverlay(url, bounds, options);
};
